package com.intexh.quickandroid.module.chat.sprinkles;

import android.content.Context;

import com.intexh.quickandroid.module.chat.sprinkles.exceptions.NoTypeSerializerFoundException;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.BooleanSerializer;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.DateSerializer;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.DoubleSerializer;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.FloatSerializer;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.IntSerializer;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.LongSerializer;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.StringSerializer;
import com.intexh.quickandroid.module.chat.sprinkles.typeserializers.TypeSerializer;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class Sprinkles {

    static Sprinkles sInstance;
    Context mContext;
    String dbName;
    int versionCode;
    List<Migration> mMigrations = new ArrayList<Migration>();
    private Map<Class, TypeSerializer> typeSerializers = new ConcurrentHashMap<Class, TypeSerializer>();

    private Sprinkles() {
        addStandardTypeSerializers();
    }

    private void addStandardTypeSerializers() {
        typeSerializers.put(int.class, new IntSerializer());
        typeSerializers.put(Integer.class, new IntSerializer());

        typeSerializers.put(long.class, new LongSerializer());
        typeSerializers.put(Long.class, new LongSerializer());

        typeSerializers.put(float.class, new FloatSerializer());
        typeSerializers.put(Float.class, new FloatSerializer());

        typeSerializers.put(double.class, new DoubleSerializer());
        typeSerializers.put(Double.class, new DoubleSerializer());

        typeSerializers.put(boolean.class, new BooleanSerializer());
        typeSerializers.put(Boolean.class, new BooleanSerializer());

        typeSerializers.put(String.class, new StringSerializer());
        typeSerializers.put(Date.class, new DateSerializer());
    }

    /**
     * Initialize sprinkles so queries and migrations can be performed
     *
     * @param context A context which is used for database operations. This context is not saved, however it's application context is.
     * @return The singleton Sprinkles instance. Use this to add migrations.
     */
    public static Sprinkles init(Context context, String dbname, int versionCode) {
        if (sInstance == null) {
            sInstance = new Sprinkles();
        }
        sInstance.mContext = context.getApplicationContext();
        sInstance.dbName = dbname;
        sInstance.versionCode = versionCode;
        return sInstance;
    }

    public static Sprinkles getInstance() {
        return sInstance;
    }

    /**
     * Used by unit tests to reset sprinkles instances between tests. This method can change at any time and should
     * never be called outside of a unit test.
     */
    public static void dropInstances() {
        sInstance = null;
        DbOpenHelper.sInstance = null;
    }

    /**
     * Add migrations to the underlying database. Every migration increments the database version.
     *
     * @param migration The migration that should be performed.
     */
    public void addMigration(Migration migration) {
        mMigrations.add(migration);
    }

    public <T> void registerType(Class<T> clazz, TypeSerializer<T> serializer) {
        typeSerializers.put(clazz, serializer);
    }

    TypeSerializer getTypeSerializer(Class<?> type) {
        if (!typeSerializers.containsKey(type)) {
            throw new NoTypeSerializerFoundException(type);
        }
        return typeSerializers.get(type);
    }

    public void execMigration(Migration migration) {
        migration.execute(DbOpenHelper.getInstance());
    }
}
